/**
 * flushCallbacks 做了三件事：
 *   1、将 pending 置为 false  表示下一个 flushCallbacks() 可以进入浏览器异步任务队列
 *   2、清空 callbacks 数组
 *   3、执行 callbacks 数组中的所有函数（比如 flushSchedulerQueue、用户调用 nextTick 传递的回调函数）
 */

/* eslint-disable */
let pending = false
let callbacks = []

// 对callbacks进行遍历，然后执行相应的回调函数
function flushCallbacks() {
	pending = false
	// 这里拷贝的原因是：
	// 有的cb 执行过程中又会往 callbacks 中加入内容
	// 比如 $nextTick的回调函数里还有$nextTick
	// 后者的应该放到下一轮的nextTick 中执行
	// 所以拷贝一份当前的，遍历执行完当前的即可，避免无休 止的执行下去
	const copies = callbcks.slice(0)
	callbacks.length = 0 // callbacks里面的函数
	for (let i = 0; i < copies.length; i++) {
		copies[i]() // 执行 callbacks 数组中的每一个函数
	}
}

let timerFunc // 异步执行函数 用于异步延迟调用 flushCallbacks 函数

// 优先使用 Promise  首选 Promise.resolve().then()
if (typeof Promise !== 'undefined' && isNative(Promise)) {
	const p = Promise.resolve()
	// 首选 Promise.resolve().then()
	timerFunc = () => {
		// 在 微任务队列 中放入 flushCallbacks 函数
		p.then(flushCallbacks)

		// IOS 的UIWebView, Promise.then 回调被推入 microTask 队列，但是队列可能不会如期执行
		// 因此，添加一个空计时器强制执行 microTask
		if (isIOS) setTimeout(noop)
	}
	isUsingMicroTask = true
} else if (
	!isIE &&
	typeof MutationObserver !== 'undefined' &&
	(isNative(MutationObserver) ||
		MutationObserver.toString === '[object MutationObserverConstructor]')
) {
	// 当 原生 Promise 不可用时，使用 原生 MutationObserver
	let counter = 1
	// 创建MO实例，监听到DOM变动后会执行回调flushCallbacks
	const observer = new MutationObserver(flushCallbacks)
	const textNode = document.createTextNode(String(counter))
	observer.observe(textNode, {
		characterData: true // 设置true 表示观察目标的改变
	})

	// 每次执行timerFunc 都会让文本节点的内容在 0/1之间切换
	// 切换之后将新值复制到 MO 观测的文本节点上
	// 节点内容变化会触发回调
	timerFunc = () => {
		counter = (counter + 1) % 2
		textNode.data = String(counter) // 触发回调
	}
	isUsingMicroTask = true
} else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
	// setImmediate 宏任务队列,  setImmediate 性能优于 setTimeout 将flushCallbacks 放入浏览器的异步任务队列
	timerFunc = () => {
		setImmediate(flushCallbacks)
	}
} else {
	timerFunc = () => {
		setTimeout(flushCallbacks, 0)
	}
}

function nextTick(cb, ctx) {
	let _resolve
	// cb 即为传入的 flushSchedulerQueue函数，会统一处理压入callbacks数组
	// callbacks也就是异步操作队列
	callbacks.push(() => {
		if (cb) {
			try {
				cb.call(ctx) // 调用 flushSchedulerQueue 函数，将其放入 callbacks异步队列
			} catch (e) {
				handleError(e, ctx, 'nextTick')
			}
		} else if (_resolve) {
			_resolve(ctx)
		}
	})

	// pending 为false 说明本轮事件循环中没有执行过timerFunc()
	if (!pending) {
		pending = true
		timerFunc()
	}

	// 当不传入 cb 参数时，提供一个promise化的调用
	if (!cb && typeof Promise !== 'undefined') {
		// 如nextTick().then(() => {})
		// 当_resolve执行时，就会跳转到then逻辑中
		return new Promise(resolve => {
			_resolve = resolve
		})
	}
}
